﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq.Expressions;

using FMZJS.Model;

namespace FMZJS.Data
{
    public class EfRepository<T> where T : BaseEntity
    {
        private readonly AppDbContext _context;
        private IDbSet<T> _entities;

        /// <summary>
        /// 构造器
        /// </summary>
        /// <param name="context"></param>
        public EfRepository(AppDbContext context)
        {
            this._context = context;
        }

        public T GetById(object id)
        {
            return this.Entities.Find(id);
        }

        public void Insert(T entity)
        {
            try
            {
                if (entity == null)
                {
                    throw new ArgumentNullException("entity");
                }

                try
                {
                    this.Entities.Add(entity);

                    this._context.SaveChanges();
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                {
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;
                    }
                }
                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                throw fail;
            }
        }

        public void Update(T entity)
        {
            try
            {
                if (entity == null)
                {
                    throw new ArgumentNullException("entity");
                }

                this.Entities.Attach(entity);
                this._context.SetEntityState(entity, EntityState.Modified);

                this._context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                {
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        msg += Environment.NewLine +
                               string.Format("Property: {0} Error: {1}", validationError.PropertyName,
                                             validationError.ErrorMessage);
                    }
                }

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                throw fail;
            }
        }

        public void Delete(T entity)
        {
            try
            {
                if (entity == null)
                {
                    throw new ArgumentNullException("entity");
                }

                this.Entities.Attach(entity);
                this.Entities.Remove(entity);

                this._context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                {
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
                    }
                }

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                throw fail;
            }
        }

        /// <summary>
        /// 查询数据
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        public virtual IQueryable<T> Query(Expression<Func<T, bool>> filter)
        {
            return Table.Where<T>(filter).AsNoTracking();
        }

        /// <summary>
        /// 分页查询数据
        /// </summary>
        /// <param name="where">条件</param>
        /// <param name="orderBy">排序字段</param>
        /// <param name="ascending">是否为升序</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页显示数量</param>
        /// <param name="totalRecord">总记录数</param>
        /// <returns></returns>
        public virtual List<T> ListByPage<TKey>(Expression<Func<T, bool>> where, Expression<Func<T, TKey>> orderBy, bool ascending, int pageIndex, int pageSize, out int totalRecord)
        {
            totalRecord = 0;

            //where = where.And(u => u.Flag != (int)Flags.Delete);

            var list = Table.Where(where).AsNoTracking();

            totalRecord = list.Count();

            if (totalRecord <= 0) return new List<T>();

            if (ascending)
            {
                list = list.OrderBy(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            else
            {
                list = list.OrderByDescending(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }

            return list.ToList();
        }

        /// <summary>
        /// 执行查询语句，返回一个实体集
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="sql">SQL语句</param>
        /// <param name="parameters">参数</param>
        /// <returns>Result</returns>
        public IEnumerable<T> SqlQuery<T>(string sql, params object[] parameters)
        {
            return _context.SqlQuery<T>(sql, parameters);
        }

        /// <summary>
        /// 执行一个 DDL/DML 命令
        /// </summary>
        /// <param name="sql">命令语句</param>
        /// <param name="timeout">超时时间（单位秒），默认值为NULL</param>
        /// <param name="parameters">参数</param>
        /// <returns>返回影响行数</returns>
        public int ExecuteSqlCommand(string sql, int? timeout = null, params object[] parameters)
        {
            if (parameters != null)
            {
                return _context.ExecuteSqlCommand(sql, timeout, parameters);
            }
            else
            {
                return _context.ExecuteSqlCommand(sql, timeout);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public virtual IQueryable<T> Table
        {
            get
            {
                //if (this.Entities.Count() <= 0)
                //{
                //    return this.Entities;
                //}
                //else
                //{
                return this.Entities.AsNoTracking();
                //}
            }
        }

        private IDbSet<T> Entities
        {
            get
            {
                if (_entities == null)
                {
                    _entities = _context.Set<T>();
                }

                return _entities;
            }
        }
    }
}
